#include <winrtsock.h>
#include <vector>

int InitializeSockAddrInfo(const char *host, int port, int family, int type, struct sockaddr_storage *ai_addr, int *ai_family, int *ai_socktype, int *ai_protocol)
{
	int status = 0;
	struct addrinfo *result = 0;
	struct addrinfo *ptr = 0;
	struct addrinfo hints;
	char p[21];

	::_itoa_s(port, p, sizeof(p), 10);
	/* hints address info structure */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	hints.ai_socktype = type;
	hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
	hints.ai_flags = AI_PASSIVE;

	/* Performs getaddrinfo */
	if (!(status = ::getaddrinfo(host, p, &hints, &result)))
	{
		/* Find our address. */
		for (ptr = result; ptr; ptr = ptr->ai_next)
		{
			/* Only IPv4 and IPv6 are supported */
			if (ptr->ai_family != AF_INET6 && ptr->ai_family != AF_INET)
			{
				continue;
			}

			if (ai_addr)
				memcpy(ai_addr, ptr->ai_addr, ptr->ai_addrlen);
			if (ai_family)
				*ai_family = ptr->ai_family;
			if (ai_socktype)
				*ai_socktype = ptr->ai_socktype;
			if (ai_protocol)
				*ai_protocol = ptr->ai_protocol;

			/* We prefer IPv4 but IPv6 can also work */
			if (ptr->ai_family == AF_INET)
			{
				break;
			}
		}
	}

	::freeaddrinfo(result);
	return status;
}

int GetFamily(const char* host, int port)
{
	int ret = AF_UNSPEC;
	if (host)
	{
		int status;
		char srv[21];
		struct addrinfo *result = NULL;
		struct addrinfo hints;

		/* set the port: used as the default service */
		if (port)
		{
			::_itoa_s(port, srv, sizeof(srv), 10);
		}
		else
		{
			memset(srv, '\0', sizeof(srv));
		}

		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_DGRAM;
		hints.ai_protocol = IPPROTO_UDP;

		if (!(status = ::getaddrinfo(host, srv, &hints, &result)))
		{
			/* Get the First result. */
			if (result)
			{
				ret = result->ai_family;
			}
		}
		::freeaddrinfo(result);
	}

	return ret;
}

int ConnectTo(SOCKET socket, const struct sockaddr_storage *to)
{
	HRESULT hr;
	int status = 0;
	if ((hr = ::WSAConnect(socket, (LPSOCKADDR)to, sizeof(*to), NULL, NULL, NULL, NULL)) == SOCKET_ERROR)
	{
		status = WSAGetLastError();
		if (status == WSAEWOULDBLOCK || status == WSAEISCONN || status == WSAEINTR || status == WSAEINPROGRESS)
		{
			status = 0;
		}
	}
	return status;
}

int GetSocketIpAndPort(const struct sockaddr *addr, char *ip, int ipLength, int *port)
{
	int status = -1;
	::ZeroMemory(ip, ipLength);
	if (addr->sa_family == AF_INET)
	{
		const struct sockaddr_in *sin = (const struct sockaddr_in *)addr;
		if (port)
		{
			*port = ::ntohs(sin->sin_port);
			status = 0;
		}
		if (ip)
		{
			status = ::getnameinfo((const struct sockaddr*)sin, sizeof(*sin), ip, ipLength, 0, 0, NI_NUMERICHOST);
		}
	}
	else if (addr->sa_family == AF_INET6)
	{
		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr;
		if (port)
		{
			*port = ::ntohs(sin6->sin6_port);
			status = 0;
		}
		if (ip)
		{
			status = ::getnameinfo((const struct sockaddr*)sin6, sizeof(*sin6), ip, ipLength, 0, 0, NI_NUMERICHOST);
		}
	}

	return status;
}

